<?php
namespace Orqlog\Yacampaign\Domain\Service;

use Orqlog\Yacampaign\Exception\LackDependencyException;
use Orqlog\Yacampaign\Domain\Repository\PrizeRepositoryInterface;
use Orqlog\Yacampaign\Domain\Repository\CampaignRepositoryInterface;
use Orqlog\Yacampaign\Domain\Repository\QualifyPolicyRepositoryInterface;
use Orqlog\Yacampaign\Exception\IllegalArgumentException;

abstract class CampaignService  implements CampaignServiceInterface
{

    /**
     * @var \Orqlog\Yacampaign\Domain\Reposistory\PrizeRepositoryInterface
     */
    protected $prizeRepository;

    /**
     * @var \Orqlog\Yacampaign\Domain\Reposistory\QualifyPolicyRepositoryInterface
     */
    protected $qualifyPolicyRepository;

    /**
     * @var Orqlog\Yacampaign\Domain\Reposistory\CampaignRepositoryInterface
     */
    protected $campaignRepository;


    /**
     * Inject PrizeRepository 
     */
    public function setQualifyPolicyRepository(QualifyPolicyRepositoryInterface $qualifyPolicyRepository) :void
    {
        $this->qualifyPolicyRepository = $qualifyPolicyRepository;
    }

    /**
     * Inject PrizeRepository 
     */
    public function setPrizeRepository(PrizeRepositoryInterface $prizeRepository) :void
    {
        $this->prizeRepository = $prizeRepository;
    }
    
    /**
     * Inject CampaignRepository
     */
    public function setCampaignRepository(CampaignRepositoryInterface $campaignRepository)
    {
        $this->campaignRepository = $campaignRepository;
    }

    /**
     * Determeine whether the use is qualified to join
     */
    public function isQualified(int $userId, int $campaignId):bool 
    {
        if (!isset($this->qualifyPolicyRepository) || ! $this->qualifyPolicyRepository instanceof QualifyPolicyRepositoryInterface) {
            throw new LackDependencyException("Please provide QualifyPolicyRepositoryInterface implementaion", 1592791557);
        }

        $policies = $this->qualifyPolicyRepository->getQualifyPoliciesForCampaign($campaignId);
        foreach ($policies as $policy) {
            $qualifyPolicyService = $this->getQualifyPolicyService(get_class($policy));
            $tResult = $qualifyPolicyService->isQualified($userId);
            if (!$tResult) {
                return false;
            }
        }

        return true;
    }

    /**
     * get QualifyPolicyService
     * 
     * for \my\package\Domain\Model\FancyQualifyPolicy
     * the corresponding service should be \my\package\Domain\Service\FancyQualifyPolicyService
     * 
     */
    public function getQualifyPolicyService(string $fqClass): Object
    {
        $parts = explode('\\', $fqClass);
        $len = count($parts);
        $fPath = '/';
        for ($i = 0; $i < $len - 2; $i++) {
            $fPath .= $parts[$i];
        }
        $fPath .= '/Service/' . $parts[$len-1] . 'Service';

        if (!class_exists($fPath)) {
            throw new IllegalArgumentException($fPath . '" does not exist', '1592796992');
        }
        $service = new $fPath();

        return $service;
    }

    /**
     * Get the prizes associated with this campain
     */
    public function getPrizesForCampaign(int $campaignId) :array 
    {
        if (!$this->prizeRepository instanceof PrizeRepositoryInterface) {
            throw new LackDependencyException("Please provide PrizeRepositoryInterface implementaion", 1592279109);
        }

        return $this->prizeRepository->getPrizesForCampaign($campaignId);
    }

    /**
     * Create prize from raw data and attach the prize to campaign
     */
    public function addPrize(int $campaignId, array $prizeData) 
    {
        if (!isset($prizeData['id']) && !$this->prizeRepository instanceof PrizeRepositoryInterface) {
            throw new LackDependencyException("Please provide PrizeRepositoryInterface implementaion", 1592279109);
        }

        if (!$this->campaignRepository instanceof CampaignRepositoryInterface) {
            throw new LackDependencyException("Please provide CampaignRepositoryInterface implementaion", 1592279579);
        }

        if (!isset($prizeData['id'])) {
            $prizeId = $this->prizeRepository->createPrize($prizeData);
        } else {
            $prizeId = $prizeData['id'];
        }
        $this->campaignRepository->attachPrize($campaignId, $prizeId);
    }
    
    /**
     * remove the association betwween the prize and campaign
     */
    public function removePrize(int $campaignId, int $prizeId)
    {
        if (!$this->campaignRepository instanceof CampaignRepositoryInterface) {
            throw new LackDependencyException("Please provide CampaignRepositoryInterface implementaion", 1592279579);
        }

        $this->campaignRepository->detachPrize($campaignId, $prizeId);
    }

    /**
     * Add qualifyPolicies
     */
    public function addQualifyPolicy(int $campaignId, array $qualifyPolicyData) :void
    {
        if (!isset($qualifyPolicyData['id']) && !$this->qualifyPolicyRepository instanceof QualifyPolicyRepositoryInterface) {
            throw new LackDependencyException("Please provide QualifyRepositoryInterface implementaion", 1592532168);
        }

        if (!$this->campaignRepository instanceof CampaignRepositoryInterface) {
            throw new LackDependencyException("Please provide CampaignRepositoryInterface implementaion", 1592279579);
        }

        if (!isset($qualifyPolicyData['id'])) {
            $qualifyPolicyId = $this->qualifyPolicyRepository->createPolicy($qualifyPolicyData);
        } else {
            $qualifyPolicyId = $qualifyPolicyData['id'];
        }
        $this->campaignRepository->attachQualifyPolicy($campaignId, $qualifyPolicyId);
    }

    /**
     * remove qualifyPolicy
     */
    public function removeQualifyPolicy(int $campaignId, int $policyId) :void
    {
        if (!$this->campaignRepository instanceof CampaignRepositoryInterface) {
            throw new LackDependencyException("Please provide CampaignRepositoryInterface implementaion", 1592279579);
        }

        $this->campaignRepository->detachQualifyPolicy($campaignId, $policyId);
    }



    /**
     * The subclass should implement this method
     */
    public abstract function decidePrize(int $userId, int $campaignId) :array;
    
}